/*
 * reuse-data.c
 *
 *  Created on: Sep 15, 2010
 *      Author: thienlong
 */

#include "reuse_object.h"
#include <stdlib.h>
#include "stringbuffer.h"

array_pool* apool_new(int typenum) {
	array_pool *cache = calloc(1, sizeof(array_pool));
	apool_init(cache, typenum);
	return cache;
}

void apool_init(array_pool *cache, int typenum) {
	cache->typenum = typenum;
	cache->ptr = calloc(typenum, sizeof(array_pointer));
	int i = 0;
	for (; i < typenum; ++i) {
		cache->ptr[i].items = vector_new(16, NULL, mem_free, NULL, 0);
	}
}

void apool_setcb(array_pool *pool, data_type type, free_data_cb free_fn,
		reset_data_cb reset_fn) {
	pool->ptr[STRING_BUFFER_TYPE].free_cb = stringbuffer_free;
	pool->ptr[STRING_BUFFER_TYPE].reset_cb = stringbuffer_reset;
	pool->ptr[STRING_BUFFER_ARRAY_TYPE].free_cb = stringbuffer_array_free;
	pool->ptr[STRING_BUFFER_ARRAY_TYPE].reset_cb = stringbuffer_array_reset;
}

void* apool_get(array_pool *cache, data_type type) {
	if (type >= 0 && type < cache->typenum) {
		array_pointer *data = (cache->ptr + type);
		void *item = vector_pop(data->items);
		if (item && data->reset_cb) {
			data->reset_cb(item);
		}
		return item;
	}
	return NULL;
}
/**
 * put a given element to cache. The cache must be not NULL.
 * If a given %data = NULL this function does nothing.
 */
void apool_put(array_pool *cache, data_type type,
		void *data) {
	if (!data)
		return;
	if (type >= 0 && type < cache->typenum) {
		vector_add(cache->ptr[type].items, data);
	}
}

int apool_element_size(array_pool *cache, data_type type) {
	return cache->ptr[type].items->size;
}

int apool_size(array_pool *cache) {
	int type = 0;
	int counter = 0;
	for (; type < cache->typenum; ++type) {
		counter += cache->ptr[type].items->size;
	}
	return counter;
}

void apool_free(array_pool *cache) {
	int type = 0;
	for (; type < cache->typenum; ++type) {
		vector_free(&(cache->ptr[type].items));
	}
	free(cache->ptr);
	free(cache);
}

#define OBJECT_POOL_ELEMENT_STATUS(data_type, typestring)	\
case data_type:	\
			counter = apool_element_size(cache, type);	\
			total += counter;	\
			stringbuffer_append_string(buf, typestring);	\
			stringbuffer_append_long(buf, counter);	\
			break;

void apool_to_string(array_pool *cache, stringbuffer *buf) {
	int counter = 0;
	int total = 0;
	int type = 0;
	for (; type < cache->typenum; ++type) {
		switch (type) {
		;
		OBJECT_POOL_ELEMENT_STATUS(STRING_BUFFER_ARRAY_TYPE, ", stringbuffer_array: ")
		;
		OBJECT_POOL_ELEMENT_STATUS(STRING_BUFFER_TYPE, ", stringbuffer: ")
		;
		}
	}
	stringbuffer_append_string(buf, ", total: ");
	stringbuffer_append_long(buf, total);
}

linked_pool* lpool_new(int typenum) {
	linked_pool *cache = calloc(1, sizeof(linked_pool));
	lpool_init(cache, typenum);
	return cache;
}

void lpool_init(linked_pool *cache, int typenum) {
	cache->typenum = typenum;
	cache->ptr = calloc(typenum, sizeof(linked_pointer));
	cache->ptr[STRING_BUFFER_TYPE].free_cb = stringbuffer_free;
	cache->ptr[STRING_BUFFER_TYPE].reset_cb = stringbuffer_reset;
	cache->ptr[STRING_BUFFER_ARRAY_TYPE].free_cb = stringbuffer_array_free;
	cache->ptr[STRING_BUFFER_ARRAY_TYPE].reset_cb = stringbuffer_array_reset;
}

void* lpool_get_element(linked_pool *cache, data_type type) {
	if (type >= 0 && type < cache->typenum) {
		linked_pointer *data = (cache->ptr + type);
		struct next_data *head = data->next;
		if (head) {
			data->next = head->next;
			head->next = NULL;
			if (data->reset_cb) {
				data->reset_cb(head);
			}
			return head;
		}
	}
	return NULL;
}
/**
 * put a given element to cache. The cache must be not NULL.
 * If a given %data = NULL this function does nothing.
 */
void lpool_put_element(linked_pool *cache, data_type type,
		struct next_data *data) {
	if (!data)
		return;
	if (type >= 0 && type < cache->typenum) {
		data->next = cache->ptr[type].next;
		cache->ptr[type].next = data;
	}
}

int lpool_element_size(linked_pool *cache, data_type type) {
	int counter = 0;
	struct next_data *item = cache->ptr[type].next;
	while (item != NULL) {
		++counter;
		item = item->next;
	}
	return counter;
}

int lpool_size(linked_pool *cache) {
	int type = 0;
	int counter = 0;
	for (; type < cache->typenum; ++type) {
		counter += lpool_element_size(cache, type);
	}
	return counter;
}

void lpool_free(linked_pool *cache) {
	struct next_data *item = NULL;
	struct next_data *next = NULL;
	int type = 0;
	for (; type < cache->typenum; ++type) {
		item = cache->ptr[type].next;
		while (item != NULL) {
			next = item->next;
			free(item);
			item = next;
		}
	}
	free(cache->ptr);
	free(cache);
}

#define DATA_CACHE_ELEMENT_STATUS(data_type, typestring)	\
case data_type:	\
			counter = lpool_element_size(cache, type);	\
			total += counter;	\
			stringbuffer_append_string(buf, typestring);	\
			stringbuffer_append_long(buf, counter);	\
			break;

void lpool_to_string(linked_pool *cache, stringbuffer *buf) {
	int counter = 0;
	int total = 0;
	int type = 0;
	for (; type < cache->typenum; ++type) {
		switch (type) {
		DATA_CACHE_ELEMENT_STATUS(STRING_BUFFER_TYPE, ", stringbuffer: ");
		}
	}
	stringbuffer_append_string(buf, ", total: ");
	stringbuffer_append_long(buf, total);
}

